#include "../Inc/TimeUtils.h"

#ifndef __DATE__
#error "__DATE__ is not defined by the compiler!"
#endif

/**
 * @brief Convert a 3-letter English month abbreviation to month number.
 * @param [in] monthStr Pointer to 3-character month abbreviation (e.g., "Jan").
 * @return Month number (1-12), or 0 if invalid.
 */
static uint8_t GetMonthFromStr(const char *monthStr)
{
    if (monthStr[0] == 'J' && monthStr[1] == 'a' && monthStr[2] == 'n') return 1;
    if (monthStr[0] == 'F' && monthStr[1] == 'e' && monthStr[2] == 'b') return 2;
    if (monthStr[0] == 'M' && monthStr[1] == 'a' && monthStr[2] == 'r') return 3;
    if (monthStr[0] == 'A' && monthStr[1] == 'p' && monthStr[2] == 'r') return 4;
    if (monthStr[0] == 'M' && monthStr[1] == 'a' && monthStr[2] == 'y') return 5;
    if (monthStr[0] == 'J' && monthStr[1] == 'u' && monthStr[2] == 'n') return 6;
    if (monthStr[0] == 'J' && monthStr[1] == 'u' && monthStr[2] == 'l') return 7;
    if (monthStr[0] == 'A' && monthStr[1] == 'u' && monthStr[2] == 'g') return 8;
    if (monthStr[0] == 'S' && monthStr[1] == 'e' && monthStr[2] == 'p') return 9;
    if (monthStr[0] == 'O' && monthStr[1] == 'c' && monthStr[2] == 't') return 10;
    if (monthStr[0] == 'N' && monthStr[1] == 'o' && monthStr[2] == 'v') return 11;
    if (monthStr[0] == 'D' && monthStr[1] == 'e' && monthStr[2] == 'c') return 12;
    return 0;
}

/**
 * @brief Check if a year is a leap year.
 * @param [in] year Year value.
 * @return 1 if leap year, 0 otherwise.
 */
static uint32_t IsLeapYear(uint32_t year)
{
    return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}

/**
 * @brief Calculate the day number in the year.
 * @param [in] year Year value.
 * @param [in] month Month value (1-12).
 * @param [in] day Day value (1-31).
 * @return Day of the year (1-366).
 */
static uint32_t DayOfYear(uint32_t year, uint8_t month, uint8_t day)
{
    static const uint32_t daysBeforeMonth[12] = {
        0, 31, 59, 90, 120, 151,
        181, 212, 243, 273, 304, 334
    };
    uint32_t dayOfYear = daysBeforeMonth[month - 1] + day;
    if (month > 2 && IsLeapYear(year))
    {
        dayOfYear += 1;
    }
    return dayOfYear;
}

/**
 * @brief Calculate the day of the week.
 * @param [in] year Year value.
 * @param [in] month Month value (1-12).
 * @param [in] day Day value.
 * @return Day of week (0=Sunday, 1=Monday, ..., 6=Saturday).
 */
static uint32_t WeekDay(uint32_t year, uint8_t month, uint8_t day)
{
    if (month < 3)
    {
        month += 12;
        year -= 1;
    }
    uint32_t K = year % 100;
    uint32_t J = year / 100;
    uint32_t h = (day + 13 * (month + 1) / 5 + K + K / 4 + J / 4 + 5 * J) % 7;

    uint32_t d = (h + 6) % 7;
    return d;
}

/**
 * @brief Get the ISO-like week number in the year.
 * @param [in] year Year value.
 * @param [in] month Month value.
 * @param [in] day Day value.
 * @return Week number (1-53).
 */
uint8_t GetWeekNumber(uint32_t year, uint8_t month, uint8_t day)
{
    uint32_t dayOfYear = DayOfYear(year, month, day);
    uint32_t firstWeekDay = WeekDay(year, 1, 1);
    if (firstWeekDay == 0)
    {
        firstWeekDay = 7;
    }

    uint8_t weekNum = (dayOfYear + firstWeekDay - 2) / 7 + 1;
    return weekNum;
}

/**
 * @brief Calculate years passed since 1990.
 * @param [in] year Year value.
 * @return Number of years since 1990.
 */
uint32_t GetYearsSince1990(uint32_t year)
{
    if (year < 1990)
    {
        return 0;
    }
    return (year - 1990);
}

/**
 * @brief Get build date from __DATE__ macro.
 * @param [out] year Pointer to store year.
 * @param [out] month Pointer to store month.
 * @param [out] day Pointer to store day.
 */
void GetBuildDate(uint32_t *year, uint8_t *month, uint8_t *day)
{
    const char *dateStr = __DATE__;
    *month = GetMonthFromStr(dateStr);
    if (dateStr[4] == ' ')
    {
        *day = dateStr[5] - '0';
    }
    else
    {
        *day = (dateStr[4] - '0') * 10 + (dateStr[5] - '0');
    }
    *year = (dateStr[7] - '0') * 1000 +
            (dateStr[8] - '0') * 100 +
            (dateStr[9] - '0') * 10 +
            (dateStr[10] - '0');
}
